package br.ufrn.btest;

import java.util.HashSet;
import java.util.Set;

import br.ufrn.btest.decorators.predicates.MyPredicate;
import br.ufrn.btest.parser.Operation;
import br.ufrn.btest.utils.PredicateTools;

public class Partitioner {

	
	private Operation operation;
	
	
	public Partitioner(Operation operation) {
		this.operation = operation;
	}
	
	
	
	public Operation getOperation() {
		return this.operation;
	}
	
	
	
	public Set<String> getOperationInputSpace() {
		Set<String> inputSpace = new HashSet<String>();
		inputSpace.addAll(getRelatedVariables(this.operation));
		inputSpace.addAll(this.operation.getIfCommandVariables());
		return inputSpace;
	}
	
	
	
	private Set<String> getRelatedVariables(Operation operation) {
		Set<String> relatedVariables = new HashSet<String>();

		relatedVariables.addAll(this.operation.getPrecondition().getVariables());
		relatedVariables.removeAll(this.operation.getReturnVariables());
		relatedVariables.addAll(getRelatedVariablesOnInvariants(relatedVariables));
		removeConstants(relatedVariables);

		return relatedVariables;
	}
	
	
	
	private Set<String> getRelatedVariablesOnInvariants(Set<String> relatedVariables) {
		Set<String> relatedInvariantVariables = new HashSet<String>(relatedVariables);
		Set<String> oldRelatedVariables = new HashSet<String>();
		
		while (!relatedInvariantVariables.equals(oldRelatedVariables)) {
			oldRelatedVariables.addAll(relatedInvariantVariables);
			searchMoreRelatedVariables(relatedInvariantVariables);
		}
		
		return relatedInvariantVariables;
	}
	
	
	
	private void searchMoreRelatedVariables(Set<String> relatedVariables) {
		Set<MyPredicate> invariant = this.operation.getMachine().getCondensedInvariantFromAllMachines();
		
		for (MyPredicate clause : invariant) {
			Set<String> clauseVariables = clause.getVariables();
			for (String variable : clauseVariables) {
				if (relatedVariables.contains(variable)) {
					relatedVariables.addAll(clauseVariables);
					break;
				}
			}
		}
	}
	
	
	
	private void removeConstants(Set<String> relatedVariables) {
		if (this.operation.getMachine().getSets() != null) {
			Set<String> setElements = this.operation.getMachine().getSets().getElementsFromAllSets();
			relatedVariables.removeAll(setElements);
		}
	}
	
	
	
	public Set<String> getOperationCharacteristics() {
		Set<String> characteristics = new HashSet<String>();

		Set<String> generalCharacteristics = PredicateTools.convertSetOfClausesToSetOfStrings(getGeneralCharacteristicsClauses());
		Set<String> conditionalCharacteristics = this.operation.getConditionalCharacteristics();
		
		characteristics.addAll(generalCharacteristics);
		characteristics.addAll(conditionalCharacteristics);
		
		return characteristics;
	}
	
	
	
	Set<Characteristic> getGeneralCharacteristicsClauses() {
		Set<Characteristic> characteristics = new HashSet<Characteristic>();
		
		for(MyPredicate clause : getPredicatesRelatedWithVariables(getOperationInputSpace(), this.operation.getPreconditionClauses())) {
			characteristics.add(new Characteristic(clause, CharacteristicType.PRE_CONDITION));
		}
		
		for(MyPredicate clause : getPredicatesRelatedWithVariables(getOperationInputSpace(), this.operation.getMachine().getCondensedInvariantFromAllMachines())) {
			characteristics.add(new Characteristic(clause, CharacteristicType.INVARIANT));
		}
		
		for(MyPredicate clause : getPredicatesRelatedWithVariables(getOperationInputSpace(), this.operation.getMachine().getPropertiesClauses())) {
			characteristics.add(new Characteristic(clause, CharacteristicType.PROPERTIES));
		}
		
//		HashSet<MyPredicate> characsClauses = new HashSet<MyPredicate>();
//		
//		characsClauses.addAll(getPredicatesRelatedWithVariables(getOperationInputSpace(), this.operation.getPreconditionClauses()));
//		characsClauses.addAll(getPredicatesRelatedWithVariables(getOperationInputSpace(), this.operation.getMachine().getCondensedInvariantFromAllMachines()));
//		characsClauses.addAll(getPredicatesRelatedWithVariables(getOperationInputSpace(), this.operation.getMachine().getPropertiesClauses()));
		
		return characteristics;
	}
	

	
	private Set<MyPredicate> getPredicatesRelatedWithVariables(Set<String> variables, Set<MyPredicate> predicates) {
		Set<MyPredicate> relatedPredicates = new HashSet<MyPredicate>();
		
		for (MyPredicate clause : predicates) {
			for(String variable : variables) {
				if (clause.getVariables().contains(variable)) {
					relatedPredicates.add(clause);
					break;
				}
			}
		}
		
		return relatedPredicates;
	}


	
	public Set<String> getTypingClauses() {
		Set<String> typingClauses = new HashSet<String>();
		
		for(String variable : getOperationInputSpace()) {
			for (Characteristic clause : getGeneralCharacteristicsClauses()) {
				if(clause.getPredicate().isTypingClause() && clause.getPredicate().getVariables().contains(variable)) {
					typingClauses.add(clause.toString());
				}
			}
		}
		
		return typingClauses;
	}
}
